#include <psp2/ctrl.h>

#include "scec2c.h"
#include "menu.h"
#include "scr_print.h"
#include "utils.h"
#include "config.h"
#include "main.h"

/* 要显示的列表条目个数，弹窗高度由此动态获取 */
#define MAX_ITEMS_DRAW_NUM 10u

#define TITLE_PADDING_L 10u
#define TITLE_PADDING_T 8u
#define TITLE_HEIGHT (fontGetFontSize() + TITLE_PADDING_T * 2)

#define ITEMVIEW_PADDING_L 10u
#define ITEMVIEW_PADDING_T 3u
#define ITEMVIEW_HEIGHT (fontGetFontSize() + ITEMVIEW_PADDING_T * 2)

#define LISTVIEW_PADDING_L 0u
#define LISTVIEW_PADDING_T 6u
#define LISTVIEW_HEIGHT (ITEMVIEW_HEIGHT * MAX_ITEMS_DRAW_NUM + LISTVIEW_PADDING_T * 2)

#define WINDOW_PADDING 0u
#define WINDOW_WIDTH 250u
#define WINDOW_HEIGHT (TITLE_HEIGHT * 2 + LISTVIEW_HEIGHT + WINDOW_PADDING * 2)
#define WINDOW_SX ((scrPrintGetScreenWidth() - WINDOW_WIDTH) / 2)
#define WINDOW_SY ((scrPrintGetScreenHeight() - WINDOW_HEIGHT) / 2)
#define WINDOW_DX (WINDOW_SX + WINDOW_WIDTH)
#define WINDOW_DY (WINDOW_SY + WINDOW_HEIGHT)

#define TITLE_SX (WINDOW_SX + WINDOW_PADDING)
#define TITLE_SY (WINDOW_SY + WINDOW_PADDING)
#define TITLE_DX (WINDOW_DX - WINDOW_PADDING)
#define TITLE_DY (TITLE_SY + TITLE_HEIGHT)
#define TITLE_WIDTH (TITLE_DX - TITLE_SX)

#define LISTVIEW_SX (WINDOW_SX + WINDOW_PADDING)
#define LISTVIEW_SY TITLE_DY
#define LISTVIEW_DX (WINDOW_DX - WINDOW_PADDING)
#define LISTVIEW_DY (LISTVIEW_SY + LISTVIEW_HEIGHT)
#define LISTVIEW_WIDTH (LISTVIEW_DX - LISTVIEW_SX)

#define ITEMVIEW_WIDTH (LISTVIEW_WIDTH - LISTVIEW_PADDING_L * 2)

#define TITLE2_SX TITLE_SX
#define TITLE2_SY LISTVIEW_DY
#define TITLE2_DX TITLE_DX
#define TITLE2_DY WINDOW_DY

#define COLOR_WINDOW_BG 0xFF7A4423
#define COLOR_TITLE_BG 0xFFA15135
#define COLOR_TEXT_FG_DEF WHITE
#define COLOR_TEXT_BG_DEF 0u
#define COLOR_ITEM_BG_SEL 0xFF4776F1

static int saveConfigToDefault();

static char *no_yes_names[] = {
    "否",
    "是",
};

static char *clock_mode_names[] = {
    "游戏默认",
    "普通模式",
    "性能模式",
    "自定义",
};
#define N_COLCK_MODE_NAMES (sizeof(clock_mode_names) / sizeof(char *))

/*********************      0   1    2    3    4    5    6  (0位置为默认值) */
int g_cpu_clock_values[] = {0, 111, 166, 222, 333, 444, 500};
int g_bus_clock_values[] = {0, 111, 166, 222};
int g_gpu_clock_values[] = {0, 111, 166, 222};
int g_xbr_clock_values[] = {0, 111, 166};

static int clock_mode_values[][4] = {
    {0, 0, 0, 0}, // 游戏默认  cpu  bus  gpu  xbar
    {4, 3, 1, 1}, // 普通模式 {333, 222, 111, 111}
    {5, 3, 3, 2}, // 性能模式 {444, 222, 222, 166}
    {0, 0, 0, 0}, // 自定义 (无用参，防bug)
};

StrsOption no_yes_option = {no_yes_names, sizeof(no_yes_names) / sizeof(char *), COLOR_TEXT_FG_DEF};

StrsOption clock_mode_option = {clock_mode_names, N_COLCK_MODE_NAMES - 1, COLOR_TEXT_FG_DEF};
IntsOption cpu_clock_ints_option = {g_cpu_clock_values, sizeof(g_cpu_clock_values) / sizeof(int), "%d MHz", COLOR_TEXT_FG_DEF};
IntsOption bus_clock_ints_option = {g_bus_clock_values, sizeof(g_bus_clock_values) / sizeof(int), "%d MHz", COLOR_TEXT_FG_DEF};
IntsOption gpu_clock_ints_option = {g_gpu_clock_values, sizeof(g_gpu_clock_values) / sizeof(int), "%d MHz", COLOR_TEXT_FG_DEF};
IntsOption xbr_clock_ints_option = {g_xbr_clock_values, sizeof(g_xbr_clock_values) / sizeof(int), "%d MHz", COLOR_TEXT_FG_DEF};

enum ColckItemIndex
{
    CLOCK_ITEM_INDEX_MODE,
    CLOCK_ITEM_INDEX_CPU,
    CLOCK_ITEM_INDEX_BUS,
    CLOCK_ITEM_INDEX_GPU,
    CLOCK_ITEM_INDEX_XBR,
};

ItemEntry clock_items[] = {
    {"快捷设置", ITEM_TYPE_OPTION_STRS, COLOR_TEXT_FG_DEF, &g_setting_config.clock_mode, &clock_mode_option, 0},
    {"CPU 频率", ITEM_TYPE_OPTION_INTS, COLOR_TEXT_FG_DEF, &g_setting_config.cpu_clock, &cpu_clock_ints_option, 0},
    {"BUS 频率", ITEM_TYPE_OPTION_INTS, COLOR_TEXT_FG_DEF, &g_setting_config.bus_clock, &bus_clock_ints_option, 0},
    {"GPU 频率", ITEM_TYPE_OPTION_INTS, COLOR_TEXT_FG_DEF, &g_setting_config.gpu_clock, &gpu_clock_ints_option, 0},
    {"XBR 频率", ITEM_TYPE_OPTION_INTS, COLOR_TEXT_FG_DEF, &g_setting_config.xbr_clock, &xbr_clock_ints_option, 0},
};
ListEntry clock_list = {"频率设置", clock_items, sizeof(clock_items) / sizeof(ItemEntry), NULL, 0, 1, 0, 0};

ItemEntry display_items[] = {
    {"显示FPS", ITEM_TYPE_OPTION_STRS, COLOR_TEXT_FG_DEF, &g_setting_config.show_fps, &no_yes_option, 0},
    {"显示电量", ITEM_TYPE_OPTION_STRS, COLOR_TEXT_FG_DEF, &g_setting_config.show_battery, &no_yes_option, 0},
    {"显示CPU信息", ITEM_TYPE_OPTION_STRS, COLOR_TEXT_FG_DEF, &g_setting_config.show_cpu, &no_yes_option, 0},
    {"显示内存信息", ITEM_TYPE_OPTION_STRS, COLOR_TEXT_FG_DEF, &g_setting_config.show_memory, &no_yes_option, 0},
    {"显示错误信息", ITEM_TYPE_OPTION_STRS, COLOR_TEXT_FG_DEF, &g_setting_config.show_error, &no_yes_option, 0},
};
ListEntry display_list = {"显示设置", display_items, sizeof(display_items) / sizeof(ItemEntry), NULL, 0, 1, 0, 0};

ItemEntry about_items[] = {
    {"中文显示示范用户插件版", ITEM_TYPE_TEXT, ORANGE, NULL, NULL, 0},
    {"\t作者: 一直改", ITEM_TYPE_TEXT, ORANGE, NULL, NULL, 0},
};
ListEntry about_list = {"关于", about_items, sizeof(about_items) / sizeof(ItemEntry), NULL, 1, 0, 0, 0};

ItemEntry main_items[] = {
    {"频率设置", ITEM_TYPE_LIST_ENTRY, COLOR_TEXT_FG_DEF, NULL, &clock_list, 0},
    {"显示设置", ITEM_TYPE_LIST_ENTRY, COLOR_TEXT_FG_DEF, NULL, &display_list, 0},
    {"重置所有设置", ITEM_TYPE_CALLBACK, COLOR_TEXT_FG_DEF, NULL, resetConfig, 0},
    {"保存设置为通用", ITEM_TYPE_CALLBACK, COLOR_TEXT_FG_DEF, NULL, saveConfigToDefault, 0},
    {"关于", ITEM_TYPE_LIST_ENTRY, COLOR_TEXT_FG_DEF, NULL, &about_list, 0},
};
ListEntry main_list = {"中文显示用户插件", main_items, sizeof(main_items) / sizeof(ItemEntry), NULL, 0, 1, 0, 0};

static ListEntry *current_list = &main_list;
static uint8_t show_menu = 0;

static int saveConfigToDefault()
{
    return saveConfigEx(CONFIG_TYPE_DEFAULT);
}

static void updataClockTextColor()
{
    /* 非默认值时改变颜色 */
    if (g_setting_config.cpu_clock == 0)
        cpu_clock_ints_option.color = COLOR_TEXT_FG_DEF;
    else
        cpu_clock_ints_option.color = CHARTREUSE;

    if (g_setting_config.bus_clock == 0)
        bus_clock_ints_option.color = COLOR_TEXT_FG_DEF;
    else
        bus_clock_ints_option.color = CHARTREUSE;

    if (g_setting_config.gpu_clock == 0)
        gpu_clock_ints_option.color = COLOR_TEXT_FG_DEF;
    else
        gpu_clock_ints_option.color = CHARTREUSE;

    if (g_setting_config.xbr_clock == 0)
        xbr_clock_ints_option.color = COLOR_TEXT_FG_DEF;
    else
        xbr_clock_ints_option.color = CHARTREUSE;
}

static void updateClockOptions()
{
    static uint8_t clock_options_inited = 0;

    if (!clock_options_inited || clock_items[CLOCK_ITEM_INDEX_MODE].option_changed)
    {
        if (g_setting_config.clock_mode != N_COLCK_MODE_NAMES - 1)
        {
            g_setting_config.cpu_clock = clock_mode_values[g_setting_config.clock_mode][0];
            g_setting_config.bus_clock = clock_mode_values[g_setting_config.clock_mode][1];
            g_setting_config.gpu_clock = clock_mode_values[g_setting_config.clock_mode][2];
            g_setting_config.xbr_clock = clock_mode_values[g_setting_config.clock_mode][3];
        }
    }

    if (clock_items[CLOCK_ITEM_INDEX_CPU].option_changed || clock_items[CLOCK_ITEM_INDEX_BUS].option_changed ||
        clock_items[CLOCK_ITEM_INDEX_GPU].option_changed || clock_items[CLOCK_ITEM_INDEX_XBR].option_changed)
    {
        if (g_setting_config.cpu_clock != 0 || g_setting_config.bus_clock != 0 ||
            g_setting_config.gpu_clock != 0 || g_setting_config.xbr_clock != 0)
            g_setting_config.clock_mode = N_COLCK_MODE_NAMES - 1;
        else
            g_setting_config.clock_mode = 0;
    }

    updataClockTextColor();
    // refreshClockFreqs();

    clock_options_inited = 1;
}

static void updateOptions()
{
    if (current_list == &clock_list)
        updateClockOptions();
}

void updateMenuConfig()
{
    updateClockOptions();
}

static void moveUp(ListEntry *list)
{
    ItemEntry *items = list->items;
    int *list_pos;
    int old_pos, max_pos;

    if (list->read_only)
    {
        list->focus_pos = 0;
        list_pos = &list->top_pos;
        max_pos = list->n_items - MAX_ITEMS_DRAW_NUM;
    }
    else
    {
        list_pos = &list->focus_pos;
        max_pos = list->n_items - 1;
    }
    if (max_pos < 0)
        max_pos = 0;

    old_pos = *list_pos;
    if (old_pos < 0)
        old_pos = 0;
    else if (old_pos > max_pos)
        old_pos = max_pos;
    do
    {
        if (*list_pos > 0)
            (*list_pos)--;
        else if (list->move_loop)
            *list_pos = max_pos;
    } while (!list->read_only && *list_pos != old_pos &&
             (items[*list_pos].type == ITEM_TYPE_TEXT ||
              items[*list_pos].type == ITEM_TYPE_OPTION_STR ||
              items[*list_pos].type == ITEM_TYPE_OPTION_INT));

    if (list->read_only)
        refreshListPosNoFocus(&list->top_pos, list->n_items, MAX_ITEMS_DRAW_NUM);
    else
        refreshListPos(&list->top_pos, &list->focus_pos, list->n_items, MAX_ITEMS_DRAW_NUM);
}

static void moveDown(ListEntry *list)
{
    ItemEntry *items = list->items;
    int *list_pos;
    int old_pos, max_pos;

    if (list->read_only)
    {
        list->focus_pos = 0;
        list_pos = &list->top_pos;
        max_pos = list->n_items - MAX_ITEMS_DRAW_NUM;
    }
    else
    {
        list_pos = &list->focus_pos;
        max_pos = list->n_items - 1;
    }
    if (max_pos < 0)
        max_pos = 0;

    old_pos = *list_pos;
    if (old_pos < 0)
        old_pos = 0;
    else if (old_pos > max_pos)
        old_pos = max_pos;
    do
    {
        if (*list_pos < max_pos)
            (*list_pos)++;
        else if (list->move_loop)
            *list_pos = 0;
    } while (!list->read_only && *list_pos != old_pos &&
             (items[*list_pos].type == ITEM_TYPE_TEXT ||
              items[*list_pos].type == ITEM_TYPE_OPTION_STR ||
              items[*list_pos].type == ITEM_TYPE_OPTION_INT));

    if (list->read_only)
        refreshListPosNoFocus(&list->top_pos, list->n_items, MAX_ITEMS_DRAW_NUM);
    else
        refreshListPos(&list->top_pos, &list->focus_pos, list->n_items, MAX_ITEMS_DRAW_NUM);
}

int isShowingMenu()
{
    return show_menu;
}

int openMenu()
{
    scrPrintInit();
    show_menu = 1;
    return 0;
}

int closeMenu()
{
    show_menu = 0;
    scrPrintFinish();
    saveConfig();
    return 0;
}

void drawMenu()
{
    char *name2;
    uint32_t name2_color;
    char name_buf[64];
    int sx, sy, dx, dy;
    int w;
    ListEntry *list;
    ItemEntry *items;
    void *item_option;
    int item_option_type;

    list = current_list;
    items = list->items;

    /* 设置文字颜色 */
    scrPrintSetBackColor(COLOR_TEXT_BG_DEF);
    scrPrintSetTextColor(COLOR_TEXT_FG_DEF);

    /* 窗口背景 */
    scrPrintRectangle(WINDOW_SX, WINDOW_SY, WINDOW_WIDTH, WINDOW_HEIGHT, COLOR_WINDOW_BG);

    /* 顶部标题 */
    scrPrintRectangle(TITLE_SX, TITLE_SY, TITLE_WIDTH, TITLE_HEIGHT, COLOR_TITLE_BG);
    scrPrintfText(TITLE_SX + TITLE_PADDING_L, TITLE_SY + TITLE_PADDING_T, list->title);
    w = scrPrintGetTextWidth(g_titleid);
    scrPrintfText(ALIGN_RIGHT(TITLE_DX - TITLE_PADDING_L, w), TITLE_SY + TITLE_PADDING_T, g_titleid);
    /* 底部标题 */
    scrPrintRectangle(TITLE2_SX, TITLE2_SY, TITLE_WIDTH, TITLE_HEIGHT, COLOR_TITLE_BG);
    if (list->previous)
        scrPrintfText(TITLE2_SX + TITLE_PADDING_L, TITLE2_SY + TITLE_PADDING_T, "%s返回", CTRL_CANCEL_TEXT);
    else
        scrPrintfText(TITLE2_SX + TITLE_PADDING_L, TITLE2_SY + TITLE_PADDING_T, "%s退出", CTRL_CANCEL_TEXT);
    snprintf(name_buf, 16, "%s确定", CTRL_ENTER_TEXT);
    w = scrPrintGetTextWidth(name_buf);
    scrPrintfText(ALIGN_RIGHT(TITLE2_DX - TITLE_PADDING_L, w), TITLE2_SY + TITLE_PADDING_T, name_buf);

    /* 选项列表 */
    sx = LISTVIEW_SX + LISTVIEW_PADDING_L;
    sy = LISTVIEW_SY + LISTVIEW_PADDING_T;
    dx = LISTVIEW_DX - LISTVIEW_PADDING_L;
    dy = LISTVIEW_DY - LISTVIEW_PADDING_T;

    scrPtrintEnableClipping(sx, sy, dx, dy);

    int i;
    for (i = list->top_pos; i < list->n_items; i++)
    {
        if (sy >= dy)
            break;

        if (!list->read_only && i == list->focus_pos)
            scrPrintRectangle(sx, sy, ITEMVIEW_WIDTH, ITEMVIEW_HEIGHT, COLOR_ITEM_BG_SEL);

        scrPrintSetTextColor(items[i].color);
        if (items[i].name)
            scrPrintfText(sx + ITEMVIEW_PADDING_L, sy + ITEMVIEW_PADDING_T, items[i].name);

        item_option = items[i].option;
        item_option_type = items[i].type;
        name2 = NULL;
        name2_color = items[i].color;

        if (item_option_type == ITEM_TYPE_OPTION_CALLBACK)
        {
            CallbackOption *option = (CallbackOption *)item_option;
            item_option = option->option;
            item_option_type = option->option_type;
        }

        if (item_option_type == ITEM_TYPE_OPTION_STR)
        {
            StrOption *option = (StrOption *)item_option;
            name2 = option->name;
            name2_color = option->color;
        }
        else if (item_option_type == ITEM_TYPE_OPTION_STRS)
        {
            StrsOption *option = (StrsOption *)item_option;
            int value = *(items[i].value);
            name2 = option->names[value];
            name2_color = option->color;
        }
        else if (item_option_type == ITEM_TYPE_OPTION_INT)
        {
            IntOption *option = (IntOption *)item_option;
            int value = *(option->value);
            snprintf(name_buf, 64, option->format, value);
            name2 = name_buf;
            name2_color = option->color;
        }
        else if (item_option_type == ITEM_TYPE_OPTION_INTS)
        {
            IntsOption *option = (IntsOption *)item_option;
            int value = *(items[i].value);
            int value2 = option->values[value];
            snprintf(name_buf, 64, option->format, value2);
            name2 = name_buf;
            name2_color = option->color;
        }
        else if (item_option_type == ITEM_TYPE_OPTION_INT_STEP)
        {
            IntStepOption *option = (IntStepOption *)item_option;
            int value = *(items[i].value);
            snprintf(name_buf, 64, option->format, value);
            name2 = name_buf;
            name2_color = option->color;
        }

        if (name2)
        {
            scrPrintSetTextColor(name2_color);
            w = scrPrintGetTextWidth(name2);
            scrPrintfText(ALIGN_RIGHT(dx - ITEMVIEW_PADDING_L, w), sy + ITEMVIEW_PADDING_T, name2);
        }

        sy += ITEMVIEW_HEIGHT;
    }

    scrPtrintDisableClipping();
}

void ctrlMenu(SceCtrlData *ctrl, int negative)
{
    ListEntry *list = current_list;
    ItemEntry *items = list->items;

    readPad(ctrl, negative);

    if (isKeyReleased(CTRL_CANCEL_KEY))
    {
        if (list->previous)
            current_list = list->previous;
        else
            closeMenu();
        goto END;
    }
    else if (isKeyReleased(SCE_CTRL_SQUARE))
    {
        closeMenu();
        goto END;
    }

    if (isKeyPressed(SCE_CTRL_UP))
    {
        moveUp(list);
    }
    else if (isKeyPressed(SCE_CTRL_DOWN))
    {
        moveDown(list);
    }

    /* 如果当前列表为只读，跳过设置变更处理 */
    if (list->read_only)
        goto END;

    void *item_option = items[list->focus_pos].option;
    int item_option_type = items[list->focus_pos].type;

    /* 具有函数回调，并且带有选项设置的双重条目 */
    if (item_option_type == ITEM_TYPE_OPTION_CALLBACK)
    {
        CallbackOption *option = (CallbackOption *)item_option;
        item_option = option->option;
        item_option_type = option->option_type;
    }

    if (isKeyPressed(SCE_CTRL_LEFT))
    {
        if (item_option_type == ITEM_TYPE_OPTION_STRS)
        {
            StrsOption *option = (StrsOption *)item_option;
            if (*(items[list->focus_pos].value) > 0)
                (*(items[list->focus_pos].value))--;
            else
                *(items[list->focus_pos].value) = option->n_names - 1;
            items[list->focus_pos].option_changed = 1;
            goto END;
        }
        else if (item_option_type == ITEM_TYPE_OPTION_INTS)
        {
            IntsOption *option = (IntsOption *)item_option;
            if (*(items[list->focus_pos].value) > 0)
                (*(items[list->focus_pos].value))--;
            else
                *(items[list->focus_pos].value) = option->n_values - 1;
            items[list->focus_pos].option_changed = 1;
            goto END;
        }
        else if (item_option_type == ITEM_TYPE_OPTION_INT_STEP)
        {
            IntStepOption *option = (IntStepOption *)item_option;
            if (*(items[list->focus_pos].value) >= option->min + option->step)
                *(items[list->focus_pos].value) -= option->step;
            else
                *(items[list->focus_pos].value) = option->max;
            items[list->focus_pos].option_changed = 1;
            goto END;
        }
    }
    else if (isKeyPressed(SCE_CTRL_RIGHT))
    {
        if (item_option_type == ITEM_TYPE_OPTION_STRS)
        {
            StrsOption *option = (StrsOption *)item_option;
            if (*(items[list->focus_pos].value) < option->n_names - 1)
                (*(items[list->focus_pos].value))++;
            else
                *(items[list->focus_pos].value) = 0;
            items[list->focus_pos].option_changed = 1;
            goto END;
        }
        else if (item_option_type == ITEM_TYPE_OPTION_INTS)
        {
            IntsOption *option = (IntsOption *)item_option;
            if (*(items[list->focus_pos].value) < option->n_values - 1)
                (*(items[list->focus_pos].value))++;
            else
                *(items[list->focus_pos].value) = 0;
            items[list->focus_pos].option_changed = 1;
            goto END;
        }
        else if (item_option_type == ITEM_TYPE_OPTION_INT_STEP)
        {
            IntStepOption *option = (IntStepOption *)item_option;
            if (*(items[list->focus_pos].value) <= option->max - option->step)
                *(items[list->focus_pos].value) += option->step;
            else
                *(items[list->focus_pos].value) = option->min;
            items[list->focus_pos].option_changed = 1;
            goto END;
        }
    }
    else if (isKeyPressed(SCE_CTRL_TRIANGLE))
    {
        if (items[list->focus_pos].value)
        {
            *(items[list->focus_pos].value) = 0;
            items[list->focus_pos].option_changed = 1;
            goto END;
        }
    }
    else if (isKeyPressed(CTRL_ENTER_KEY))
    {
        item_option = items[list->focus_pos].option;
        item_option_type = items[list->focus_pos].type;

        if (item_option_type == ITEM_TYPE_CALLBACK)
        {
            int (*callback)() = (int (*)())item_option;
            if (callback)
                callback();
        }
        else if (item_option_type == ITEM_TYPE_OPTION_CALLBACK)
        {
            CallbackOption *option = (CallbackOption *)item_option;
            if (option->callback)
                option->callback();
        }
        else if (item_option_type == ITEM_TYPE_LIST_ENTRY)
        {
            ListEntry *next = (ListEntry *)item_option;
            if (next->read_only)
            {
                next->top_pos = next->focus_pos = 0;
            }
            else
            {
                next->top_pos = 0;
                next->focus_pos = -1;
                moveDown(next);
            }
            next->previous = list;
            current_list = next;
        }
    }

END:
    /* 如果设置有变更，回调设置变更处理函数 */
    if (items[list->focus_pos].option_changed)
    {
        updateOptions();
        items[list->focus_pos].option_changed = 0;
    }

    /* 将hook返回的ctrl->buttons值清空 */
    ctrl->buttons = negative ? 0xFFFFFFFF : 0;
}